<?php 

/**
 *  查询构建器
 * @author hwz
 */
class MongoLQueryBuilder {

    /**
     * 查询条件
     *
     * @var array 
     */
    protected $conditions;

    /**
     * 仅操作单个数据的标记
     *
     * @var string
     */
    protected $getOne=false;

    /**
     * 模型名称
     *
     * @var string
     */
    protected $class;

    /**
     * 集合对象
     *
     * @var MongoCollection
     */
    protected $collection;

    /**
     * 查询的默认选项
     *
     * @var array
     */
    protected $options = array(
        'autoIncrement' => false,
        'softDelete'    => false,
        'timestamp'     => false,
    );

    /**
     * 构造
     * $param string $class
     * $param MongoCollection
     * $param array $options
     * @return void
     * @author hwz
     **/
    public function __construct($class,$collection,$options = array()) {

        $this->conditions = array(
            'where'=>array(),
            'limit'=>0,
            'skip'=>0,
            'order'=>array(),
            'fields'=>array(),
        );
        $this->collection = $collection;
        $this->class = $class;
        $this->options = array_merge($this->options,$options);
    }

    /**
     * 查找一个数据 
     *
     * @param int $id 主键 
     * @param string|array $fields 引空格隔开的字符串或者数组
     * @return MogoLModel子类实例，否则为Null
     * @author hwz
     **/
    public function find($id,$fields = array()) {
        $this->fields($fields);
        if ($this->options['autoIncrement']) {
            $id = intval($id);
        }
        $result = $this->collection->findOne(
            array('_id'=>$id),
            $this->conditions['fields']
        );
        if ($result) {
            $model = new $this->class($result);
            return $model;
        }
        return null;
    }

    /**
     * 设置返回字段
     *
     * @param string|array $fields 引空格隔开的字符串或者数组
     * @return MongoLQueryBuilder
     * @author hwz
     **/
    public function fields($fields) {
        if (is_string($fields)) {
            $this->conditions['fields'] = preg_split('/\s/',$fields);
        }elseif(!empty($fields)){
            $this->conditions['fields'] = $fields;
        }
        return $this;
    }

    /**
     * 跳过多少数据
     *
     * @param int $count 指定数量
     * @return MongoLQueryBuilder
     * @author hwz
     **/
    public function skip($count) {
        $this->conditions['skip'] = $count;
        return $this;
    }

    /**
     * 获取多少记录
     *
     * @param int $limit 数量
     * @return MongoLQueryBuilder
     * @author hwz
     **/
    public function take($limit) {
        $this->conditions['limit'] = $limit;
        return $this;
    }

    /**
     * 条件设置
     *
     * @param array $where
     * @return MongoLQueryBuilder
     * @author hwz
     **/
    public function where(array $where) {
        if(!isset($this->conditions['where'])){
            $this->conditions['where'] = array();
        }
        $this->conditions['where'] = array_merge($this->conditions['where'],$where);
        return $this;
    }

    /**
     * orderBy
     * @param string $field 
     * @param int $asc
     * @return MongoLQueryBuilder
     * @author hwz
     **/
    public function orderBy($field, $asc = 0) {
        $this->conditions['order'][$field] = $asc;
        return $this;
    }

    /**
     * 执行查询,查询单条记录返回MongoLModel,多条返回MongolCollection
     *
     * @param $fields
     * @return MongoLCollection|MongoLModel
     * @author hwz
     **/
    public function get($fields=array()) {

        $this->fields($fields);

        if ($this->getOne) {
            $this->getOne = false;
            return $this->getOne();
        }

        $cursor = $this->fetching();

        return new MongoLCollection(iterator_to_array($cursor,false),$this->class);
    }

    /**
     * all
     * @return void
     * @author hwz
     **/
    public function all() {
        $cursor = $this->collection->find();
        return new MongoLCollection(iterator_to_array($cursor,false),$this->class);
    }

    /**
     * fetching
     * @return MongoCursor
     * @author hwz
     **/
    public function fetching() {
        $table = $this->collection;
        $cursor = $table->find($this->conditions['where'],$this->conditions['fields']);

        if ($this->conditions['skip']) {
            $cursor->skip($this->conditions['skip']);
        }

        if ($this->conditions['limit']) {
            $cursor->limit($this->conditions['limit']);
        }

        if ($this->conditions['order']) {
            $cursor->sort($this->conditions['order']);
        }
        return $cursor;
    }

    /**
     * 获得记录总数
     * 
     * @return int 此处忽略skip limit
     * @author hwz
     **/
    public function count() {
        return $this->fetching()->count();
    }

    /**
     * getOne
     * @param $fields
     * @return void
     * @author hwz
     **/
    public function getOne($fields=array()) {

        $this->fields($fields);

        $row = $this->collection->findOne(
            $this->conditions['where'],
            $this->conditions['fields']
        );
        if (!$row) {
            return null;
        }
        return new $this->class($row);
    }

    /**
     * save
     *
     * @param array $document 要存储的文档，引用传值，插入数据会有_id
     * @return boolean 操作是否成功
     * @author hwz
     **/
    public function save(&$document) {
        if (isset($document['_id']) && $document['_id']) {
            if( $this->options['autoIncrement'] ){
                $document['_id'] = intval($document['_id']);
            }
            $this->conditions['where']['_id'] = $document['_id'];
            return $this->update($document);
        }else{
            return $this->insert($document);
        }
    }

    /**
     * 批量保存或更新
     *
     * 这里的更新还是逐个更新的
     *
     * @param array $documents 
     * @return int 成功保存的条数
     * @author hwz
     **/
    public function batchSave(array &$documents) {
        $count = 0;
        foreach ($documents as &$doc) {
            $r = $this->save($doc);
            if ($r) {
                $count ++;
            }
        }
        return $count;
    }

    /**
     * 更新文档 
     * 以$set方式更新
     *
     * @param array $set 需要更新的内容
     * @return void
     * @author hwz
     **/
    public function update($set,$multi=false) {
        unset($set['_id']);
        if ($this->options['timestamp']) {
            $set['updatetime'] = time();
        }
        $result = $this->collection->update(
            $this->conditions['where'],
            array('$set'=>$set),
            array('w'=>1,'multiple'=>$multi)
        );

        if ($result && isset($result['ok'])) {
            return (bool)$result;
        }
    }

    /**
     * 插入数据  
     * @param array $document 引用传值
     * @return boolean 是否插入成功
     * @author hwz
     **/
    public function insert(&$document) {

        if (!isset($document['_id']) || !$document['_id']) {
            $document['_id'] = $this->generateId();
        }

        if ($this->options['timestamp']) {
            $document['addtime'] = $document['updatetime'] = time();
        }
        
        $result = $this->collection
            ->insert($document,array('w'=>1));

        if ($result && isset($result['ok'])) {
            return (bool)$result;
        }
    }

    /**
     * 生成_id
     *
     * 如果模型设置了autoIncrement，则使用MongoLAutoIncrement::seq生成
     *
     * @return MongoId|int
     * @author hwz
     **/
    public function generateId() {
        // $this->_id 如果设置了ID
        /* if ($this->_id) {
            return $this->_id;
        } */
        if ($this->options['autoIncrement']) {
            return MongoLAutoIncrement::seq($this->collection->getName());
        }else{
            return new MongoId();
        }
    }

    /**
     * 删除文档 
     *
     * @param boolean * $onlyOne 默认为 false
     * @return int 成功删除数量
     * @author hwz
     **/
    public function delete($onlyOne=false) {
        $result = $this->collection->remove(
            $this->conditions['where'],
            array('w'=>1)
        );
        return $result['n'];
    }

    /**
     * delete的别名
     * @param boolean $onlyOne
     * @return int 
     * @author hwz
     **/
    public function remove($onlyOne=false) {
        return $this->delete($onlyOne);
    }

    /**
     * 标记本次查询为单调查询
     * @return $this MongoLQueryBuilder
     * @author hwz
     **/
    public function one() {
        $this->getOne = true;
        return $this;
    }

    /**
     * 将某字段自增,原子操作，有写锁,有并发优势
     *
     * @param string $field 字段名称
     * @param $step int 默认为1
     * @return void
     * @author hwz
     **/
    public function increase($field,$step=1) {

        $ret = $this->collection->findAndModify(
            $this->conditions['where'],
            array('$inc'=>array($field=>$step)),
            null,
            array('new'=>true,'upsert'=>true)
        );
        
        return $ret[$field];
    }

    /**
     * 将某字段自减,原子操作，有写锁,有并发优势
     *
     * @param string $field 字段名称
     * @param $step int 默认为1
     * @return void
     * @author hwz
     **/
    public function decrease($field,$step=1) {
        
        return $this->increase($field,-$step);
    }


    /**
     * distinct
     * @return void
     * @author hwz
     **/
    public function distinct($field) {
        return $this;
    }

    /**
     * 返回分页数据
     *
     * @param int $page 第几页
     * @param int $pagesize 
     * @param array|string 返回字段 
     * @return array
     * @author hwz
     **/
    public function pagination($page = 1,$pagesize=20,$fields=array()) {
        if (!$page) {
            $page = 1;
        }
        $this->skip( ( $page-1 ) * $pagesize )
            ->take($pagesize);
        $this->fields($fields);
        
        $count = $this->count();
        return array(
            'curPage'=>$page,
            'pageSize'=>$pagesize,
            'pageCount'=> ceil($count / $pagesize),
            'total'=>$count,
            'records'=>$this->get(),
        );
    }

}


//end of file
